package com.haiyou.common.system;

import java.io.File;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;

import org.apache.commons.lang3.Validate;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.google.common.collect.Maps;
import com.haiyou.common.event.asyc.AsyncEventBuses;
import com.haiyou.common.file.FileListeners;
import com.haiyou.common.util.date.DateUtils;
import com.haiyou.common.util.file.FileUtils;
import com.haiyou.common.util.system.SystemUtils;
import com.haiyou.common.util.yml.YmlUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * 
 * @Description: 本地配置文件读取
 * @author xingyuan
 * @date 2022年6月1日 下午8:04:52
 */
@Slf4j
public class SystemProperties {

	private static final String WINDOWS_RESOURCES_PATH = "src/main/resources";
	private static final String LINUX_RESOURCES_PATH = "resources";

	private static final String DELIMITER = File.separator;
	
	/**
	 * 文件缓存 配置信息
	 */
	private static Map<String, String> propertyCache = Maps.newConcurrentMap();
	
	public static void setProperty(String key,String value) {
		propertyCache.put(key, value);
	}
	
	public static String getProperty(String key,String defaultValue) {
		return propertyCache.getOrDefault(key, defaultValue);
	}
	
	public static String getProperty(String key) {
		return propertyCache.getOrDefault(key, null);
	}
	
	private SystemProperties() {
		
	}
	
	
	/**
	 * 默认加载 config.properties 文件
	 * @return
	 */
	static public boolean load() {
		return load(SystemProperties.getResourcesAbsolutePath("spring.path","config.properties"));
	}
	/**
	 * 加载配置文件,可以同时加载多个或一个,如果配置文件被删除,会抛出异常
	 * 
	 * @param paths
	 * @return
	 */
	static public boolean load(String... paths) {
		
		File[] files = new File[paths.length];
		for (int i = 0; i < paths.length; i++)
			files[i] = new File(paths[i]);
		
		for (File file : files) {
			
			if (ObjectUtils.isEmpty(read(file.getAbsolutePath()))) {
				log.warn("file not find {}",file.getAbsoluteFile());
				return false;
			}
			
			FileListeners.getInstance().addListener(file, f -> true, (f) -> {
				Map<String, String> modifies = read(file.getPath());
				if (ObjectUtils.isEmpty(modifies)) {
					SystemConfigModifiedEvent event = new SystemConfigModifiedEvent(file, modifies);
					log.warn("file [{}] modifies [{}]", file, modifies);
					AsyncEventBuses.getInstance().postEvent(event);
				}
			});
		} 
		
		//加载spring 配置
		loadSpringProperty();
		
		propertyCache.forEach((k, v) -> log.info("file property {}={}", k, v));
		System.getProperties().forEach((k, v) -> log.info("-D{}={}", k, v));
		return true;
	}
	
	
	private static void loadSpringProperty() {
		String path = SystemProperties.getResourcesAbsolutePath();
		File[] files = new File(path).listFiles(f ->f.getName().endsWith(".yml"));
		for (File file : files) {
			Map<String, Object> maps = YmlUtils.readYmlFile(file.getAbsolutePath());
			for (String key : maps.keySet()) {
				if(propertyCache.containsKey(key)) {
					log.warn("存在配置文件key冲突,key:{},path:{}",file.getAbsolutePath());
				}
				setProperty(key, String.valueOf(maps.get(key)));
			}
		}
	}
	
	

	static protected Map<String, String> read(String path) {
		Map<String, String> modifies = Maps.newHashMap();
		Map<String, String> result;
		try {
			try {
				result = FileUtils.readFileToMap(path);
				if (CollectionUtils.isEmpty(result)) {
					return null;
				}
			} catch (Exception e) {
				//log.error(e.getMessage(), e);
				return null;
			}
			result.forEach((k, v) -> {
				if (!propertyCache.containsKey(k) || !getProperty(k).equals(v)) {
					modifies.put(k, v);
					setProperty(k, v);// 把配置数据加入到系统参数中方便调用
				}
			});
			return modifies;
		} catch (Exception e) {
			log.error(e.getMessage() + ":" + path, e);
			return null;
		}
	}

	static public String getResourcesAbsolutePath() {
		String pathname = WINDOWS_RESOURCES_PATH;
		if (SystemUtils.isLinux())
			pathname = LINUX_RESOURCES_PATH;
		return new File(pathname).getAbsolutePath();
	}

	/**
	 * 获取(windows:src/main/resources/linux:resources)配置文件
	 * </p>
	 * key对应的value所在的绝对路径, value只需要简单的名称就可以,内部会自动根据系统类型和项目根目录补全绝对路径
	 * 
	 * @param key
	 * @return
	 */
	static public String getResourcesAbsolutePath(String key) {
		
		if(!StringUtils.hasLength(key)) {
			throw new RuntimeException("key not null");
		}
		
		String value = getString(key);
		
		if(!StringUtils.hasLength(value)) {
			throw new RuntimeException("value not null");
		}
		
		String resourcePath = getResourcesAbsolutePath() + DELIMITER + value;
		if (SystemUtils.isLinux())
			resourcePath = DELIMITER + resourcePath;
		return resourcePath;
	}

	/**
	 * 获取(windows:src/main/resources/linux:resources)配置文件
	 * </p>
	 * key对应的value所在的绝对路径, value只需要简单的名称就可以,内部会自动根据系统类型和项目根目录补全绝对路径
	 * 
	 * @param key
	 * @return
	 */
	static public String getResourcesAbsolutePath(String key, String defaultValue) {
		
		if(!StringUtils.hasLength(key)) {
			throw new RuntimeException("key not null");
		}
		
		String value = getString(key, defaultValue);
		
		if(!StringUtils.hasLength(value)) {
			throw new RuntimeException("value not null");
		}
		
		String resourcePath = getResourcesAbsolutePath() + DELIMITER + value;
		if (SystemUtils.isLinux())
			resourcePath = DELIMITER + resourcePath;
		return resourcePath;
	}
	
	

	/**
	 * 获取(windows:src/main/resources/linux:resources)配置文件
	 * </p>
	 * key对应的value所在的绝对路径, value只需要简单的名称就可以,内部会自动根据系统类型和项目根目录补全绝对路径
	 * 
	 * @param key
	 * @return
	 */
	static public String[] getResources(String key) {
		String[] array = getStringArray(key);
		String[] resourcesPath = new String[array.length];
		for (int i = 0; i < resourcesPath.length; i++)
			resourcesPath[i] = getResourcesAbsolutePath(array[i]);
		return resourcesPath;
	}

	/**
	 * 获取(windows:src/main/resources/linux:resources)配置文件
	 * </p>
	 * key对应的value所在的绝对路径, value只需要简单的名称就可以,内部会自动根据系统类型和项目根目录补全绝对路径
	 * 
	 * @param key
	 * @return
	 */
	static public String[] getResources(String key, String[] defaultArray) {
		String[] array = getStringArray(key, defaultArray);
		String[] resourcesPath = new String[array.length];
		for (int i = 0; i < resourcesPath.length; i++)
			resourcesPath[i] = getResourcesAbsolutePath(array[i]);
		return resourcesPath;
	}

	static public String getString(String key) {
		Validate.isTrue(!StringUtils.isEmpty(key));
		String value = getProperty(key);
		Validate.isTrue(value != null, "not exists value for key [%s]", key);
		return value;
	}

	static public String getString(String key, String defaultValue) {
		return getProperty(key, defaultValue);
	}

	static public String[] getStringArray(String key) {
		String s = getString(key);
		if (s.startsWith("[") && s.endsWith("]"))
			return s.substring(1, s.length() - 1).split(",");
		return new String[] { s };
	}

	static public String[] getStringArray(String key, String[] defaultArray) {
		String s = getString(key, Arrays.toString(defaultArray));
		if (s.startsWith("[") && s.endsWith("]"))
			return s.substring(1, s.length() - 1).split(",");
		return new String[] { s };
	}

	static public Integer getInteger(String key) {
		return Integer.valueOf(getString(key));
	}

	static public Integer getInteger(String key, Integer defaultValue) {
		return Integer.valueOf(getString(key, String.valueOf(defaultValue.intValue())));
	}

	static public int getIntValue(String key) {
		return Integer.parseInt(getString(key));
	}

	static public int getIntValue(String key, int defaultValue) {
		return Integer.parseInt(getString(key, String.valueOf(defaultValue)));
	}

	static public int[] getIntArray(String key) {
		String[] array = getStringArray(key);
		int[] _array = new int[array.length];
		for (int i = 0; i < array.length; i++) {
			_array[i] = Integer.parseInt(array[i].trim());
		}
		return _array;
	}

	static public int[] getIntArray(String key, int[] defaultArray) {
		String[] defArray = new String[defaultArray.length];
		for (int i = 0; i < defArray.length; i++) {
			defArray[i] = String.valueOf(defaultArray[i]);
		}
		String[] array = getStringArray(key, defArray);
		int[] _array = new int[array.length];
		for (int i = 0; i < array.length; i++) {
			_array[i] = Integer.parseInt(array[i].trim());
		}
		return _array;
	}

	static public Long getLong(String key) {
		return Long.valueOf(getString(key));
	}

	static public Long getLong(String key, Long defaultValue) {
		return Long.valueOf(getString(key, String.valueOf(defaultValue.longValue())));
	}

	static public long getLongValue(String key) {
		return Long.parseLong(getString(key));
	}

	static public long getLongValue(String key, long defaultValue) {
		return Long.parseLong(getString(key, String.valueOf(defaultValue)));
	}

	static public Double getDouble(String key) {
		return Double.valueOf(getString(key));
	}

	static public Double getDouble(String key, Double defaultValue) {
		return Double.valueOf(getString(key, String.valueOf(defaultValue.doubleValue())));
	}

	static public double getDoubleValue(String key) {
		return Double.parseDouble(getString(key));
	}

	static public double getDoubleValue(String key, double defaultValue) {
		return Double.parseDouble(getString(key, String.valueOf(defaultValue)));
	}

	static public Boolean getBoolean(String key) {
		return Boolean.valueOf(getString(key));
	}

	static public Boolean getBoolean(String key, Boolean defaultValue) {
		return Boolean.valueOf(getString(key, String.valueOf(defaultValue.booleanValue())));
	}

	static public boolean getBooleanValue(String key) {
		return Boolean.parseBoolean(getString(key));
	}

	static public boolean getBooleanValue(String key, boolean defaultValue) {
		return Boolean.parseBoolean(getString(key, String.valueOf(defaultValue)));
	}

	static public Date getDate(String key) {
		try {
			return DateUtils.timeDate(getString(key));
		} catch (Exception e) {
			log.error("getDate for [" + key + "]" + getString(key), e);
			return null;
		}
	}

	static public Date getDate(String key, Date defaultValue) {
		try {
			return DateUtils.timeDate(getString(key));
		} catch (Exception e) {
			return defaultValue;
		}
	}

	static public Class<?> getClass(String key) {
		try {
			return Class.forName(getString(key));
		} catch (Exception e) {
			log.error("getClass for[" + key + "]" + getString(key), e);
			return null;
		}
	}

	static public Class<?> getClass(String key, Class<?> defaultValue) {
		try {
			return Class.forName(getString(key));
		} catch (Exception e) {
			return defaultValue;
		}
	}

	static public Enum getEnum(String key, Class enumClazz) {
		try {
			return Enum.valueOf(enumClazz, getString(key));
		} catch (Exception e) {
			log.error("getEnum for[" + key + "] to enum [" + enumClazz.getName() + "]" + getString(key), e);
			return null;
		}
	}

	static public Enum getEnum(String key, Class enumClazz, Enum defaultValue) {
		try {
			return Enum.valueOf(enumClazz, getString(key));
		} catch (Exception e) {
			return defaultValue;
		}
	}

	static public JSONObject getJSONObject(String key) {
		try {
			return JSONObject.parseObject(getString(key));
		} catch (Exception e) {
			log.error("getJSONObject for[" + key + "]" + getString(key), e);
			return null;
		}
	}

	static public JSONObject getJSONObject(String key, JSONObject defaultValue) {
		try {
			return JSONObject.parseObject(getString(key));
		} catch (Exception e) {
			return defaultValue;
		}
	}

	static public JSONArray getJSONArray(String key) {
		try {
			return JSONArray.parseArray(getString(key));
		} catch (Exception e) {
			log.error("getJSONArray for[" + key + "]" + getString(key), e);
			return null;
		}
	}

	static public JSONArray getJSONArray(String key, JSONArray defaultValue) {
		try {
			return JSONArray.parseArray(getString(key));
		} catch (Exception e) {
			return defaultValue;
		}
	}

	static public void set(String key, String value) {
		Map<String, String> modifies = Maps.newHashMap();
		modifies.put(key, value);
		setProperty(key, value);
		SystemConfigModifiedEvent event = new SystemConfigModifiedEvent(null, modifies);
		log.warn("file [{}] modifies [{}]", null, modifies);
		AsyncEventBuses.getInstance().postEvent(event);
	}

	static public void set(Map<String, String> map) {
		Map<String, String> modifies = Maps.newHashMap(map);
		map.forEach((key, value) -> setProperty(key, value));
		SystemConfigModifiedEvent event = new SystemConfigModifiedEvent(null, modifies);
		log.warn("file [{}] modifies [{}]", null, modifies);
		AsyncEventBuses.getInstance().postEvent(event);
	}

	public static boolean has(String key) {
		if (StringUtils.isEmpty(key))
			return false;
		String s = getProperty(key);
		return !StringUtils.isEmpty(s);
	}

}
